import { OrderQueryType, OrderType, TreeChildrenResolve } from "src/config/defaultconfig";
import { EntityManager, EntityTarget, FindOptionsUtils, FindTreeOptions, ObjectLiteral, QueryRunner, SelectQueryBuilder, TreeRepository, TreeRepositoryUtils } from "typeorm";
import { isNil, pick, unset } from 'lodash';
import { getOrderByQuery } from "src/config/helper";
import { QueryParams } from "src/config/types";
export class BaseTreeRepository<E extends ObjectLiteral> extends TreeRepository<E>{
    //查询器名称
    protected _qbName = 'treeEntity'

    /**
     * 默认排序规则，可以通过每个方法的orderBy选项进行覆盖
     */
    protected orderBy?: string | { name: string; order: `${OrderType}` };

    /**
     * 删除父分类后是否提升子分类的等级
     */
    protected _childrenResolve?: TreeChildrenResolve;

    constructor(target: EntityTarget<E>, manager: EntityManager, queryRunner?: QueryRunner) {
        super(target, manager, queryRunner);
    }


    //返回查询器名称

    get qbName() {
        return this._qbName
    }
    get childrenResolve() {
        return this._childrenResolve;
    }

    //构键基础查询器
    buildBaseQB(qb?: SelectQueryBuilder<E>): SelectQueryBuilder<E> {
        const queryBuilder = qb ?? this.createQueryBuilder(this._qbName)

        return queryBuilder.leftJoinAndSelect(`${this.qbName}.parent`, 'parent')
    }

    //生成排序 的QueryBuilder

    addOrderByQuery(qb: SelectQueryBuilder<E>, orderBy?: OrderQueryType) {
        const order = orderBy ?? this.orderBy
        return isNil(order) ? getOrderByQuery(qb, this.qbName, orderBy) : qb;
    }

    //获取树
    async findTrees(options?: FindTreeOptions | QueryParams<E>) {
        const roots = await this.findRoots(options);
        await Promise.all(roots.map((root) => this.findDescendantsTree(root, options)));
        return roots;
    }

    //获取根节点
    async findRoots(options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};
        const escapeAlias = (alias: string) => this.manager.connection.driver.escape(alias)
        const escapeColumn = (column: string) => this.manager.connection.driver.escape(column)
        const JoinColumn = this.metadata.treeParentRelation.joinColumns[0]
        const name = JoinColumn.givenDatabaseName || JoinColumn.databaseName
        let qb = this.addOrderByQuery(this.buildBaseQB(), orderBy)
        qb.where(`${escapeAlias(this._qbName)}.${escapeColumn(name)} IS NULL`)
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, pick(options, ['relations', 'depth']));

        qb = addQuery ? await addQuery(qb) : qb;
        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        return qb.getMany();

    }
    async findDescendantsTree(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        let { addQuery, orderBy, withTrashed, onlyTrashed } = options
        let qb = this.buildBaseQB(this.addOrderByQuery(this.createDescendantsQueryBuilder(this.qbName, 'TreeClosure', entity)))
        qb = isNil(addQuery) ? await this.addOrderByQuery(qb) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted();
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, pick(options, ['relations', 'depth']));
        //并返回查询结果中的原始数据以及实体对象。
        let entities = await qb.getRawAndEntities()
        const relationMaps = await TreeRepositoryUtils.createRelationMaps(this.manager, this.metadata, this.qbName, entities.raw)
        TreeRepositoryUtils.buildChildrenEntityTree(this.metadata, entity, entities.entities, relationMaps, {
            depth: -1,
            ...pick(options, ['relations']),
        },)

        return entity
    }

    //查询祖先树
    async findAncestorsTree(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};
        let qb = this.buildBaseQB(this.addOrderByQuery(this.createDescendantsQueryBuilder(this.qbName, 'treeClosure', entity)))
        qb = isNil(addQuery) ? await addQuery(this.addOrderByQuery(qb)) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);

        }

        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, options)
        let entities = await qb.getRawAndEntities()
        const relationMaps = TreeRepositoryUtils.createRelationMaps(this.manager, this.metadata, 'treeEntity', entities.raw)


        TreeRepositoryUtils.buildParentEntityTree(this.metadata, entity, entities.entities, relationMaps)
        return entity
    }
    //获取后代元素
    async findDescendants(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};

        let qb = this.buildBaseQB(this.createDescendantsQueryBuilder(this.qbName, 'TreeClosure', entity))

        qb = addQuery ? await addQuery(this.addOrderByQuery(qb)) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, options)

        return qb.getMany()




    }

    //获取后代元素数量
    async countDescendants(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};

        let qb = this.buildBaseQB(this.createDescendantsQueryBuilder(this.qbName, 'TreeClosure', entity))

        qb = addQuery ? await addQuery(this.addOrderByQuery(qb)) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, options)

        return qb.getCount()




    }
    //获取祖先元素
    async findAncestors(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};

        let qb = this.buildBaseQB(this.createAncestorsQueryBuilder(this.qbName, 'TreeClosure', entity))

        qb = addQuery ? await addQuery(this.addOrderByQuery(qb)) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, options)

        return qb.getMany()




    }
    //获取祖先元素数量
    async countAncestors(entity: E, options?: FindTreeOptions & QueryParams<E>) {
        const { addQuery, orderBy, withTrashed, onlyTrashed } = options ?? {};

        let qb = this.buildBaseQB(this.createAncestorsQueryBuilder(this.qbName, 'TreeClosure', entity))

        qb = addQuery ? await addQuery(this.addOrderByQuery(qb)) : this.addOrderByQuery(qb, orderBy)

        if (withTrashed) {
            qb.withDeleted()
            if (onlyTrashed) qb.where(`${this.qbName}.deletedAt IS NOT NULL`);
        }
        FindOptionsUtils.applyOptionsToTreeQueryBuilder(qb, options)

        return qb.getCount()

    }
    async toFlatTrees(trees: E[], depth = 0, parent: E | null = null) {

        let FlatTree: Omit<E, 'children'>[] = []

        for (const item of trees) {
            (item as any).depth = depth,
                (item as any).parent = parent
            let Flat = { ...item }
            delete item.children
            FlatTree.push(Flat)
            //判断是否还还有子类
            if (item.children) {
                let a = await this.toFlatTrees(item.children, depth + 1, item)
                FlatTree.push(...a)

            }
        }

        return FlatTree
    }


}